<template>
    <div class="creat_box" @click="sumbit"
        :style="{ 'background-color': background ? background : 'rgb(234, 234, 235)' }">
        <div v-show="status !== 2" style="width:100%;height: 100%;">
            <div v-if="defaultImg" class="img_box">
                <img :src="defaultImg" :style="ImgStyle">
            </div>
            <slot name="context" :status="status" v-else>
                {{ context[status] }}
            </slot>
        </div>
        <div v-show="status === 2" class="img_box">
            <slot name="result" :status="status" :imgUrl="imgData?.data.url">
                <img :src="imgData.data.url" v-if="imgData" :style="ImgStyle">
                <span v-else>
                    图片加载失败
                </span>
            </slot>
        </div>

    </div>
    <input @change="finish" ref="input" type="file" style="display:none" />
</template>

<script lang='ts'>
import { ref, defineComponent, PropType, StyleValue } from 'vue'
import axios from 'axios'

export type beforeUpload = (from: File) => true | string
export type onErr = (msg: string) => void
export type finish = (result: result) => void

export interface result {
    code: number;
    msg: string;
    data: {
        _id: string;
        url: string;
        createdAt: string;
    };
}
export default defineComponent({
    name: 'upLoad',
    emits: ['finish'],
    props: {
        url: {
            type: String,
            required: true
        },
        background: String,
        beforeUpload: Function as PropType<beforeUpload | undefined>,
        onErr: Function as PropType<onErr | undefined>,
        onFinish: Function as PropType<finish | undefined>,
        defaultImg: String,
        ImgStyle: Object as PropType<StyleValue | undefined>
    },
    setup({ url, beforeUpload, onErr, onFinish }, { emit }) {
        const status = ref<0 | 1 | 2 | 3>(0)
        const context = ['单击上传', '上传中', '上传完成', '上传失败,请重试']
        const input = ref<HTMLInputElement | null>(null)
        const imgData = ref<result | null>(null)
        const sumbit = () => {
            if (input.value) {
                input.value.click()
            }
        }
        const finish = (e: Event) => {
            const target = e.target as HTMLInputElement
            if (target.files) {
                const from = new FormData()
                const files = Array.from(target.files)

                if (beforeUpload) {
                    const result = beforeUpload(files[0])
                    if (typeof result === 'string') {
                        if (onErr) {
                            onErr(result)
                        }
                        return
                    }
                }
                status.value = 1
                from.append('file', files[0])
                axios.post(url, from, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                }).then(success => {
                    status.value = 2
                    imgData.value = success.data
                    if (onFinish) {
                        onFinish(success.data)
                    }
                }).catch((err) => {
                    status.value = 3
                    if (onErr) {
                        onErr(err || '未知错误')
                    }
                })
            }
        }
        return { input, sumbit, finish, status, imgData, context }
    }
})
</script>
<style scoped lang="less">
* {
    color: rgb(108, 117, 125);
    font-size: 40px;
}

.creat_box,
.creat_box>div {
    width: 100%;
    height: 100%;
    cursor: pointer;
    display: flex;
    justify-content: center;
    align-items: center;
}

.img_box {
    width: 100%;
    height: 100%;
    padding: 10px;
    display: flex;
    justify-content: center;
    align-items: center;

    img {
        max-height: 100%;
        max-width: 100%;
        width: auto;
        height: auto;
    }
}
</style>